#include "framework.h"
#include <Windows.h>
#include <XAudio2.h>
#pragma comment(lib,"xaudio2.lib") 
#include "XAudio2.h"
#include <math.h>


HRESULT XAudio2::FindChunk(HANDLE hFile, DWORD fourcc, DWORD& dwChunkSize, DWORD& dwChunkDataPosition)
{
	HRESULT hr = S_OK;
	if (INVALID_SET_FILE_POINTER == ::SetFilePointer(hFile, 0, NULL, FILE_BEGIN))
		return ::HRESULT_FROM_WIN32(GetLastError());

	DWORD dwChunkType;
	DWORD dwChunkDataSize;
	DWORD dwRIFFDataSize = 0;
	DWORD dwFileType;
	DWORD bytesRead = 0;
	DWORD dwOffset = 0;

	while (hr == S_OK)
	{
		DWORD dwRead;
		if (0 == ReadFile(hFile, &dwChunkType, sizeof(DWORD), &dwRead, NULL))
			hr = HRESULT_FROM_WIN32(GetLastError());

		if (0 == ReadFile(hFile, &dwChunkDataSize, sizeof(DWORD), &dwRead, NULL))
			hr = HRESULT_FROM_WIN32(GetLastError());

		switch (dwChunkType)
		{
		case fourccRIFF:
			dwRIFFDataSize = dwChunkDataSize;
			dwChunkDataSize = 4;
			if (0 == ReadFile(hFile, &dwFileType, sizeof(DWORD), &dwRead, NULL))
				hr = HRESULT_FROM_WIN32(GetLastError());
			break;

		default:
			if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, dwChunkDataSize, NULL, FILE_CURRENT))
				return HRESULT_FROM_WIN32(GetLastError());
		}

		dwOffset += sizeof(DWORD) * 2;

		if (dwChunkType == fourcc)
		{
			dwChunkSize = dwChunkDataSize;
			dwChunkDataPosition = dwOffset;
			return S_OK;
		}

		dwOffset += dwChunkDataSize;

		if (bytesRead >= dwRIFFDataSize) return S_FALSE;

	}

	return S_OK;

}
HRESULT XAudio2::ReadChunkData(HANDLE hFile, void* buffer, DWORD buffersize, DWORD bufferoffset)
{
	HRESULT hr = S_OK;
	if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, bufferoffset, NULL, FILE_BEGIN))
		return HRESULT_FROM_WIN32(GetLastError());
	DWORD dwRead;
	if (0 == ReadFile(hFile, buffer, buffersize, &dwRead, NULL))
		hr = HRESULT_FROM_WIN32(GetLastError());
	return hr;
}


template<class Interface>
inline void XAudio2::SafeRelease(Interface*& pInterfaceToRelease) {
	if (pInterfaceToRelease != nullptr) {
		pInterfaceToRelease->Release();
		pInterfaceToRelease = nullptr;
	}
}

HRESULT XAudio2::CreateSource()
{
	HRESULT hr;
	WAVEFORMATEX wfx = {};
	wfx.nSamplesPerSec = 44100;
	wfx.nBlockAlign = 2;
	wfx.nChannels = 1;
	wfx.wFormatTag = WAVE_FORMAT_PCM;
	wfx.cbSize = sizeof(wfx);
	wfx.wBitsPerSample = 16;
	wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;

	hr = mXAudio2->CreateSourceVoice(&mSourceVoice, (WAVEFORMATEX*)&wfx);
	return hr;
}

UINT32 XAudio2::BuffersQueued()
{
	XAUDIO2_VOICE_STATE state = {};
	mSourceVoice->GetState(&state);
	return state.BuffersQueued;
}

HRESULT XAudio2::SubmitSample(SHORT* buffer, UINT32 len)
{
	HRESULT hr;
	XAUDIO2_BUFFER xbuffer = { 0 };
	xbuffer.pAudioData = reinterpret_cast<const BYTE*>(buffer);
	xbuffer.AudioBytes = len * sizeof(SHORT);
	xbuffer.pContext = nullptr;
	hr = mSourceVoice->SubmitSourceBuffer(&xbuffer);
	return hr;
}



HRESULT XAudio2::Create()
{
	HRESULT hr;
	if (FAILED(hr = ::XAudio2Create(&mXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR)))
		return hr;

	if (FAILED(hr = mXAudio2->CreateMasteringVoice(&mMasterVoice)))
		return hr;

	if (FAILED(hr = CreateSource()))
		return hr;

	//if (FAILED(hr = XA2_Play()))
	//	return hr;

	return hr;
}

void XAudio2::Release()  noexcept {
	mSourceVoice->Stop();
	if (mSourceVoice) {
		mSourceVoice->DestroyVoice();
		mSourceVoice = nullptr;
	}
	if (mMasterVoice) {
		mMasterVoice->DestroyVoice();
		mMasterVoice = nullptr;
	}
	this->SafeRelease(mXAudio2);
}

HRESULT XAudio2::Initialize()
{
	HRESULT hr;

	if (FAILED(hr = ::CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
		return hr;

	if (FAILED(hr = this->Create()))
		return hr;

	return hr;
}

HRESULT XAudio2::Play()
{
	return mSourceVoice->Start();
}
